﻿using UnityEngine;
using System.Collections;
using System.Threading;
using System.IO;
using System.Net;
using System;

namespace DashFire
{
  public class HttpDownLoader
  {
    public static int sReadWriteTimeOut = 2 * 1000;  // 超时等待时间
    public static int sTimeOutWait = 5 * 1000;       // 超时等待时间
    public static int sOneReadLen = 16384;           // 一次读取长度 16384 = 16*kb
    /// <summary>
    /// 下载方法(断点续传)
    /// </summary>
    /// <param name="url">URL下载地址</param>
    /// <param name="savePath">Save path保存路径</param>
    /// <param name="callBack">Call back回调函数</param>
    public void DownLoad(string url, string filePath, Action callBack)
    {
      mErr = null;
      IsStop = false;
      //开启子线程下载,使用匿名方法
      thread = new Thread(delegate ()
      {
        //获取下载文件的总长度
        long totalLength = GetLength(url);
        if(totalLength < 0)
        {
          mErr = "获得文件失败";
          return;
        }
        CreateDirectory(filePath);
        //使用流操作文件
        FileStream fs = CreateFile(filePath);
        //获取文件现在的长度
        long fileLength = fs.Length;
        Progress = (int)(fileLength * 100 / totalLength);
        //如果没下载完
        try
        {
          if (fileLength < totalLength)
          {
            //断点续传核心，设置本地文件流的起始位置
            fs.Seek(fileLength, SeekOrigin.Begin);
            HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
            //断点续传核心，设置远程访问文件流的起始位置
            request.AddRange((int)fileLength);
            request.Timeout = sTimeOutWait;
            request.ReadWriteTimeout = sReadWriteTimeOut;
            Stream stream = request.GetResponse().GetResponseStream();

            byte[] buffer = new byte[sOneReadLen];
            //使用流读取内容到buffer中
            //注意方法返回值代表读取的实际长度,并不是buffer有多大，stream就会读进去多少
            int length = stream.Read(buffer, 0, buffer.Length);
            while (length > 0)
            {
              if (IsStop) //下载中断
                break;
              //将内容再写入本地文件中
              fs.Write(buffer, 0, length);
              //计算进度
              fileLength += length;
              Progress = (int)(fileLength * 100 / totalLength);
              //类似尾递归
              length = stream.Read(buffer, 0, buffer.Length);
            }
            stream.Close();
            stream.Dispose();
          }
          else
          {
            Progress = 100;
          }
        }
        catch (Exception e)
        {
          mErr = e.Message;
        }

        fs.Close();
        fs.Dispose();
        //如果下载完毕，执行回调
        if (Progress == 100)
        {
          if (callBack != null) callBack();
        }
      });
      //开启子线程
      thread.IsBackground = true;
      thread.Start();
    }

    void CreateDirectory(string fileName)
    {
      int index = fileName.LastIndexOf('/');
      string dir = fileName.Substring(0, index);
      //判断保存路径是否存在
      if (!Directory.Exists(dir))
      {
        Directory.CreateDirectory(dir);
      }
    }

    FileStream CreateFile(string fileName)
    {
      //先下载到临时文件中
      return new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write);
    }
    /// <summary>
    /// 获取下载文件的大小
    /// </summary>
    /// <returns>The length.</returns>
    /// <param name="url">URL.</param>
    long GetLength(string url)
    {
      HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
      request.Method = "HEAD";
      request.Timeout = sTimeOutWait;
      request.ReadWriteTimeout = sReadWriteTimeOut;
      HttpWebResponse response = request.GetResponse() as HttpWebResponse;
      return response.ContentLength;
    }

    public void Break() //中断下载
    {
      IsStop = true;
    }
    public int Progress { get; private set; }
    public bool IsDone()
    {
      return Progress == 100 || string.IsNullOrEmpty(mErr) == false;
    }
    public string Error
    {
      get { return mErr; }
    }
    //涉及子线程要注意,Unity关闭的时候子线程不会关闭，所以要有一个标识
    private bool IsStop;
    private Thread thread;  //用子线程下载
    private string mErr = null;
  }
}
